package cn.changeforyou.analyzer.combine.orm.compare;

import cn.changeforyou.analyzer.combine.orm.XmlCodeType;
import cn.changeforyou.analyzer.javaparse.model.EntityModel;
import cn.changeforyou.analyzer.xmlparse.mybatis.model.MybatisXmlContent;
import cn.changeforyou.analyzer.xmlparse.mybatis.model.MybatisXmlLabel;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotNull;
import java.util.*;
import org.dom4j.Node;

/**
 * 实体类和表比较器
 *
 * @author zhyu
 * @date 2021/2/18 11:35
 */
@Slf4j
public class EntityAndXmlComparator {
	
	/**
	 * @param xmlEntity
	 * @param entityModel
	 * @return
	 */
	public @NotNull XmlCompareResult compare(MybatisXmlContent xmlEntity, EntityModel entityModel) {
		XmlCompareResult result = new XmlCompareResult();
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = entityModel.getClassOrInterfaceDeclaration();
		String fullyQualifiedName = classOrInterfaceDeclaration.getFullyQualifiedName().get();
		List<MybatisXmlLabel> labels = xmlEntity.getLabels();
		List<String> entityString = entityModel.getComparedStruct();
		result.setFullEntity(entityString);
		for (MybatisXmlLabel label : labels) {
			XmlCompareResultNode node = new XmlCompareResultNode();
			node.setRoot(xmlEntity.getRoot());
			List<String> labelString = label.getComparedStruct();
			ArrayList<String> labelStringBak = new ArrayList<>(labelString);
			List<String> entityStringBak = new ArrayList(entityString);
			labelStringBak.removeAll(entityString);
			entityStringBak.removeAll(labelString);
			node.setEntityMoreField(entityStringBak);
			node.setXmlLabelMoreField(labelStringBak);
			node.setNode(label.getNode());
			node.setTableName(entityModel.getTableName());
			if (entityStringBak.isEmpty() && labelStringBak.isEmpty()) {
				node.isOk = true;
			}
			
			switch (label.getId()) {
				case Alias_Column_List:
					node.setXmlCodeType(XmlCodeType.Alias_Column_List);
					break;
				case Base_Column_List:
					node.setXmlCodeType(XmlCodeType.Base_Column_List);
					break;
				case AliasResultMap:
					node.setXmlCodeType(XmlCodeType.AliasResultMap);
					break;
				case BaseResultMap:
					node.setXmlCodeType(XmlCodeType.BaseResultMap);
					break;
			}
			
			result.add(node);
		}
		result.buildLackingNode();
		
		return result;
	}
	
	
	@ToString
	public static class XmlCompareResult {
		
		@Getter
		private List<XmlCompareResultNode> resultNodes;
		@Getter
		private boolean isOk = true;
		@Setter
		private List<String> fullEntity;
		
		public XmlCompareResult() {
			resultNodes = new ArrayList<>();
		}
		
		public void buildLackingNode() {
			XmlCodeType[] values = XmlCodeType.values();
			for (XmlCodeType xmlCodeType : values) {
				if (!containsXmlCodeType(xmlCodeType)) {
					XmlCompareResultNode node = new XmlCompareResultNode();
					node.setOk(false);
					node.setEntityMoreField(fullEntity);
					node.setXmlCodeType(xmlCodeType);
					resultNodes.add(node);
				}
			}
		}
		
		/**
		 * 是否是完整的
		 */
		@Getter
		private boolean isIntact;
		
		byte typeNum = 0;
		
		public boolean containsXmlCodeType(XmlCodeType xmlCodeType) {
			return (typeNum >> xmlCodeType.getBitPosition() & 0b1) == 1;
		}
		
		public void showResult() {
			log.info("xml里面标签拥有情况， baseResultMap: {}, aliasResultMap: {}, baseColumnList: {}, aliasColumnList :{}",
				containsXmlCodeType(XmlCodeType.BaseResultMap), containsXmlCodeType(XmlCodeType.AliasResultMap),
				containsXmlCodeType(XmlCodeType.Base_Column_List), containsXmlCodeType(XmlCodeType.Alias_Column_List));
		}
		
		public void add(XmlCompareResultNode node) {
			if (null == node) {
				throw new IllegalArgumentException("参数不合法");
			}
			resultNodes.add(node);
			if (!node.isOk) {
				isOk = false;
			}
			switch (node.getXmlCodeType()) {
				case BaseResultMap:
					typeNum += 1;
					break;
				case AliasResultMap:
					typeNum += 2;
					break;
				case Base_Column_List:
					typeNum += 4;
					break;
				case Alias_Column_List:
					typeNum += 8;
					break;
			}
			if (typeNum == 15) {
				isIntact = true;
			}
		}
		
		public boolean isEmpty() {
			return resultNodes.isEmpty();
		}
	}
	
	
	@Data
	public static class XmlCompareResultNode {
		
		private Node root;
		public XmlCompareResultNode() {
		}
		
		private boolean isOk;
		
		public XmlCompareResultNode(XmlCodeType xmlCodeType) {
			this.xmlCodeType = xmlCodeType;
		}
		
		/**
		 * 差异节点类型
		 */
		private XmlCodeType xmlCodeType;
		
		public void setXmlCodeType(XmlCodeType xmlCodeType) {
			this.xmlCodeType = xmlCodeType;
			this.name = xmlCodeType.getDescription();
		}
		
		/**
		 * 差异节点id 或者应该拥有的id
		 */
		private String name;
		
		/**
		 * resultMap的type 用于对付vo
		 */
		private String voType;
		
		/**
		 * 实体类多出的字段
		 */
		private List<String> entityMoreField;
		
		/**
		 * xml标签多出的字段
		 */
		private List<String> xmlLabelMoreField;
		
		private String tableName;
		
		private Node node;
		
		@Override
		public String toString() {
			return "XmlCompareResult{" +
				       "xmlCodeType=" + xmlCodeType +
				       ", name='" + name +
				       ", \nentityMoreField=" + entityMoreField +
				       ", \nxmlLabelMoreField=" + xmlLabelMoreField +
				       ", \nisOk=" + isOk +
				       ", \nnode=" + node +
				       '}';
		}
		
		
	}
	
	
}
